﻿using System;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace Data_Storages
{
    public sealed class NetServer
    {
        private readonly Socket _serverSocket;
        private IPEndPoint _ipe;
        private Thread _acceptThread;
        private readonly List<NetServerConnectionInformation> _connections = new List<NetServerConnectionInformation>();
        private bool _isRunning;
        public event EventHandler<NetServerMessageEventArgs> MessageEvent;

        public NetServer(IPAddress ip, int port) 
        {
            _ipe = new IPEndPoint(ip, port);
            _serverSocket = new Socket(_ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            _serverSocket.Bind(_ipe);
        }

        public void Start(int maxConnections)
        {
            _isRunning = true;
            _serverSocket.Listen(maxConnections);
            _acceptThread = new Thread(AcceptConnections) {IsBackground = true};
            _acceptThread.Start();
        }

        public void Stop()
        {
            _isRunning = false;
            Thread.Sleep(100);
            _connections.Clear();
            _ipe = null;
            _serverSocket.Close();
        }

        private void AcceptConnections()
        {
            while (_isRunning)
            {
                try
                {
                    var socket = _serverSocket.Accept();
                    var connection = new NetServerConnectionInformation
                                         {
                                             Socket = socket,
                                             Thread = new Thread(ProcessConnection)
                                                          {
                                                              IsBackground
                                                                  = true
                                                          }
                                         };
                    connection.Thread.Start(connection);
                    lock (_connections) _connections.Add(connection);
                }
                catch (Exception) { }
                Thread.Sleep(100);
            }
        }

        private void ProcessConnection(object state)
        {
            var connection = (NetServerConnectionInformation)state;
            try
            {
                while (true)
                {
                    var bytesRead = 0;
                    var message = new byte[connection.Socket.Available];
                    bytesRead += connection.Socket.Receive(message, bytesRead, connection.Socket.Available, SocketFlags.None);
                    if (bytesRead > 0)
                    {
                        var ms = new MemoryStream(message);
                        var bf = new BinaryFormatter();
                        ms.Position = 0;
                        OnMessageEvent(new NetServerMessageEventArgs(message));
                    }
                    else if (bytesRead == 0)
                    {
                        return;
                    }
                }
            }
            catch (Exception exc)
            {
                throw new Exception("Error while working.", exc);
            }
            finally
            {
                connection.Socket.Close();
                lock (_connections)
                {
                    _connections.Remove(connection);
                }
            }
        }

        private void OnMessageEvent(NetServerMessageEventArgs args)
        {
            var handler = MessageEvent;
            if (handler != null)
            {
                handler(this, args);
            }
        }
    }
}
